@file:Suppress("unused")

package com.virtualightning.mvvm.impl.task

import com.virtualightning.mvvm.utils.AppUtils
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CompletableDeferred

/**
 *  Anchor : Create by CimZzz
 *  Time : 2021/11/12 11:06
 *  进度 Deferred
 *  目前仅支持将进度回调至主线程
 */

/**
 * 获取进度 Deferred
 * @return ProgressDeferred<T> 进度 Deferred
 */
fun <T> progressDeferred(): ProgressDeferred<T> {
	return ProgressDeferredImpl()
}

/**
 * 进度 Deferred 接口
 * @param T
 */
interface ProgressDeferred<T>: CompletableDeferred<T> {
	fun setProgressCallback(callback: (Float) -> Unit)
	fun notifyProgressChanged(progress: Float)
}

/**
 * 进度 Deferred 的实现类
 */
open class ProgressDeferredImpl<T>(
	private val deferred: CompletableDeferred<T> = CompletableDeferred()
): ProgressDeferred<T>, CompletableDeferred<T> by deferred {
	/**
	 * 进度百分比
	 * 0 ~ 1.0
	 */
	private var progress: Float = 0f

	/**
	 * 进度回调
	 */
	private var progressCallback: ((Float) -> Unit)? = null


	/**
	 * 设置进度回调
	 * @param callback Function1<Float, Unit> 进度回调
	 */
	override fun setProgressCallback(callback: (Float) -> Unit) {
		callback(progress)
		if(isCompleted || isCancelled) {
			return
		}
		this.progressCallback = callback
	}

	/**
	 * 通知进度发生变化
	 * @param progress Float 进度值
	 */
	override fun notifyProgressChanged(progress: Float) {
		if(isCancelled || isCompleted) {
			return
		}
		if(progress < 0f || progress > 1f) {
			// 进度值不合法
			return
		}

		progressChanged(progress)
	}

	/**
	 * 完成时使百分比为 1.0
	 * @param value T 值
	 * @return Boolean 是否改变
	 */
	override fun complete(value: T): Boolean {
		if(isCancelled || isCancelled) {
			return false
		}
		progressChanged(1f)
		return deferred.complete(value)
	}

	/**
	 * 主动取消会去掉回调引用
	 * @param cause CancellationException?
	 */
	override fun cancel(cause: CancellationException?) {
		this.progressCallback = null
		deferred.cancel(cause)
	}

	/**
	 * 进度变化回调
	 * @param progress Float 进度发生变化
	 */
	private fun progressChanged(progress: Float) {
		this.progress = progress
		if(progressCallback != null) {
			AppUtils.runOnUIThread {
				val progressCallback = progressCallback ?: return@runOnUIThread
				progressCallback.invoke(this.progress)
			}
		}
	}
}